home account info subscribe login search FAQ/help site map contact us


 
Brief Full
 Advanced
      Search
 Search Tips
To access the contents, click the chapter and section titles.

Bug Proofing Visual Basic: A Guide to Error Handling and Prevention
(Publisher: John Wiley & Sons, Inc.)
Author(s): Rod Stephens
ISBN: 0471323519
Publication Date: 11/01/98

Search this book:
 
Previous Table of Contents Next


Indent the code inside a subroutine because it executes at a different level of scope than code outside the routine. Some programmers indent variable declarations within a routine because they are at the same level of scope as the code within the routine.

Private Sub MySub()
     Dim name_counter As Integer    
     Dim student_number As Integer    
     
     For student_number = 1 To NumStudents        
	 :    
     Next student_number
End Sub

I am accustomed to keeping variable declarations at the same level as the routine’s declaration. This separates the variable declarations more firmly from the code.

Private Sub MySub()
Dim name_counter As Integer
Dim student_number As Integer    

    For student_number = 1 To NumStudents        
	:    
    Next student_number
End Sub

Either method is fine as long as you are consistent.

Remove or Assert Assumptions

Either remove or assert every assumption. For example, a subroutine that sorts the items in an array can use LBound and UBound to determine the array’s bounds. In that case, the routine may not need to make any assumptions about the array’s bounds.

However, if the routine does make assumptions about the array’s bounds, it should use Debug.Assert or Stop statements to verify that the bounds meet those assumptions. The most straightforward implementation of the heapsort algorithm, for example, assumes the lower array’s lower bound is 1. A heapsort routine should use Debug.Assert or Stop to verify that assumption.

Note that in either case, the routine assumes that the array has been allocated. LBound and UBound generate errors if the array has not yet been allocated. The following code shows how a program can protect itself if in this situation.

Private Sub SortArray(arr() As Integer)
Dim l_bound As Integer
' (Other declarations omitted)    
       :
       
       ' Verify that the array has been allocated.    
       On Error GoTo UnAllocated    
       l_bound = LBound(arr)    
       On Error GoTo 0   ' Resume normal error handling.
       ' Verify assumptions.        
	   :
       
       ' Sort the array.        
	      :    
       Exit Sub
UnAllocated:    
    If Err.Number = 9 Then
       ' The array has not been allocated.        
       Err.Raise err_UNALLOCATED_ARRAY, _            
	   "MyProgram.MapValueToArray", _            
	   "Parameter ""arr"" has not been allocated with ReDim"    
       Else
       
	  ' Reraise the unknown error.        
	  Err.Raise Err.Number, _            
	      Err.Source, Err.Description, _            
	      Err.HelpFile, Err.HelpContext    
	  End If
End Sub

Catch Invalid Situations

When a routine encounters a situation that should never arise, it should let someone know about it. For example, a subroutine that expects a variant parameter to contain a filename should never receive a parameter that contains an array of integers. That situation never makes sense and there is no reasonable way the routine can figure out what to do.

During the design phase, the routine can use Debug.Assert or Stop to make the program stop when it encounters an invalid situation. In a final program in which the debugging code has been removed, the routine should raise an error using the Err.Raise statement. While the calling routine probably cannot handle the error, it can try to continue running.

In any case, the routine should always tell someone about the error. It should not quietly ignore the error and continue.

Raise Errors for Exceptions

If a routine encounters an exceptional condition, it should raise an error. This forces the calling routine to take exceptional action using an error handler. If a routine that saves data into a file cannot open the file, that is an important and exceptional condition. It should force the calling routine to take action by raising an error.

During normal situations, if a routine must return information to the calling routine, it should return the information through a parameter passed ByRef or through its return value if it is a function. A routine should not raise an error under normal circumstances.

Consider a routine that searches a phone directory for a specific name. Depending on the user’s input, this routine may fail to find the specified person. Failing to find a person is a normal situation so the routine should not raise an error. It should return a status code indicating whether it succeeded or failed. The calling routine must check the status code to see whether it found the person it wanted.

Note that Microsoft’s Common Dialog Control violates this guideline. If a program sets the control’s CancelError property to True, the control raises an error when the user cancels a file selection operation. It would be better if the control provided a Canceled property that indicated whether the user canceled the operation.

Use Enums for Status Codes

Use enumerated types or constants to define status codes. Do not use True, False, or magic numbers. The following statement uses a function named OpenDataFile. It is not clear from the code whether OpenDataFile returns True when it is successful or when it fails.

If OpenDataFile(file_name) Then ...

In the following version it is obvious that the If statement executes its code when the function fails.

Enum ofStatus
    ofOpenFailed    
    ofOpenOk
End Enum        
	:
    If OpenDataFile(file_name) = ofOpenFailed Then ...

Using Enums for status values not only makes the code more obvious, it also makes creating new status codes easier. Suppose a function returns True to indicate success and False to indicate failure. Now suppose you later discover that the function can fail in more than one way and the calling routine needs to know how it failed. Making the function accommodate this change is difficult. If the function returns enumerated status codes instead of True and False, adding a new status code is easy.

Return the Worst Data Possible for Errors

When a routine encounters an error and returns an error status code, it should return the worst possible value. This encourages the calling routine to check the status code before it uses the return values.

For example, suppose a routine searches an array for a book’s title and returns the book’s index in the array. If it cannot find a book, the routine should return an error status code. For instance, it could set its return value to –32,767. If the programmer who wrote the calling function does not check the status code and tries to use this return value, the program will probably crash and the error will be obvious. On the other hand, if the search routine returns the value 0, the calling routine might be able to continue, depending on the lower bound of the array. This bug would be much harder to find.

Note that Visual Basic default values look quite sensible. For example, the default value for an uninitialized integer is 0. If a routine does not take specific action to return unreasonable values like –32,767, the result may look useable to the calling routine.


Previous Table of Contents Next


Products |  Contact Us |  About Us |  Privacy  |  Ad Info  |  Home

Use of this site is subject to certain Terms & Conditions, Copyright © 1996-1999 EarthWeb Inc.
All rights reserved. Reproduction whole or in part in any form or medium without express written permision of EarthWeb is prohibited.